-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Stabilize -Cmin-function-alignment
#142824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Stabilize -Cmin-function-alignment
#142824
Conversation
Some changes occurred to the CTFE machinery Some changes occurred to the CTFE / Miri interpreter cc @rust-lang/miri Some changes occurred in compiler/rustc_codegen_cranelift cc @bjorn3 Some changes occurred in compiler/rustc_codegen_ssa The Miri subtree was changed cc @rust-lang/miri |
// alignment that works on all target platforms. COFF does not support higher alignments. | ||
if bytes > 8192 { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should it be limited on all platforms? Can't we error when the alignment exceeds the maximum that the actual target we are compiling for supports? Maybe someone genuinely needs to align to 16k on ELF for whatever reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, so as we've discovered, alignment is just a mess between clang and llvm. For instance -falign-function
only goes up to 1 << 16
, although you can manually align a function to a much higher alignment.
For 8192, we know it'll work everywhere, and the logic for when to accept/reject an alignment value is clear.
This flag aligns all functions to the minimum, so I have a hard time seeing a realistic scenario where aligning all functions to 16k is a reasonable thing to do (the limit on individual functions will be handled separately).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think the options are:
- Limit to
8192
on all platforms, it's consistent and unlikely to cause issues. The limit can be safely raised in the future if a need arises - Follow
clang
and allow1 << 16
, except when the target object format is COFF, then the limit is8192
. - Accept what llvm accepts: allow
1 << 29
, except when the target object format is COFF, then the limit is8192
.
I've picked the most conservative one (again, with the option to relax the limits if the need ever arises), but if there is consensus now on one of the other options that's also fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is my understanding that on Linux kernels we still nominally support, overaligning beyond the page size, typically 4096, is not going to work properly, so it is arguable that even 8192 is too high.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO for the long term, hard-coding to 8192
across all platforms seem like a mistake -- especially because then, does this become part of the language or even more problematic, become part of (compiler/language) stability guarantees?
EDIT: or rather, what is a suitable upper bound in this case in terms of stability guarantees? As in, if we stabilize n=8192
as a universal upper bound, if we find out some platform cannot handle that in the future?
let fn_align = self.tcx.codegen_fn_attrs(instance.def_id()).alignment; | ||
let global_align = self.tcx.sess.opts.unstable_opts.min_function_alignment; | ||
let global_align = self.tcx.sess.opts.cg.min_function_alignment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(orthogonal to this PR)
it is not great that the logic for merging the per-fn alignment and the global alignment needs to be repeated in each backend.
Maybe codegen_fn_attrs
should just take min_function_alignment
into account?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, using sess.opts
in the interpreter is at best iffy since it means the interpreter can behave differently in different crates in the same crate graph which can cause unsoundness...
@rustbot labels +T-lang If the idea with this is that passing cc @rust-lang/lang |
@rust-lang/spec: What thoughts do we have about how to handle compiler flags that affect the language definition? cc @RalfJung |
Here's a question. Since this does have language-level effect, might this be better expressed as a crate-level attribute? If there's a good reason, given the use case, for this to be a compiler flag instead, probably it'd be good to describe that in some detail. |
If it can then be applied via |
…-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: rust-lang#82232 discussed in: rust-lang#142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc `@RalfJung` I think this is an improvement regardless, is there anything else that should be done for miri?
…-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: rust-lang#82232 discussed in: rust-lang#142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc ``@RalfJung`` I think this is an improvement regardless, is there anything else that should be done for miri?
Huh. That could be... interesting to work with, since it would logically apply only to entities that originate from that crate, right? |
@folkertdev Do any of our tests verify that if you
Then the resulting function is instantiated with the correct alignment? Where "correct" is... well, pick an answer, I guess? |
Rollup merge of #142854 - folkertdev:centralize-min-function-alignment, r=workingjubilee centralize `-Zmin-function-alignment` logic tracking issue: #82232 discussed in: #142824 (comment) Apply the `-Zmin-function-alignment` value to the alignment field of the function attributes when those are created, so that individual backends don't need to consider it. The one exception right now is cranelift, because it can't yet set the alignment for individual functions, but it can (and does) set the global minimum function alignment. cc ``@RalfJung`` I think this is an improvement regardless, is there anything else that should be done for miri?
☔ The latest upstream changes (presumably #142901) made this pull request unmergeable. Please resolve the merge conflicts. |
@workingjubilee we don't, currently. I think the expected answer is clear though, because I'm not sure how that would work with
Is setting and then relying on the alignment fundamentally different from |
It's about whether we like this as a safety argument: /// SAFETY: The pointee must be a 32-byte aligned function. It's OK
/// for the low-order bits of the function pointer itself to be
/// non-zero, e.g., if they're used for controlling the processor
/// mode.
unsafe fn f(x: fn()) { todo!() }
fn g() {
let p = g as fn();
// SAFETY: We compiled with `-Cmin-function-alignment=32`.
unsafe { f(p) };
} For |
Wait so, does this need to be handled like Target Modifiers? |
Higher alignments are not supported in COFF
302017f
to
2010f9a
Compare
Some changes occurred in compiler/rustc_codegen_ssa/src/codegen_attrs.rs |
well, two crates with a different minimum alignment can work together just fine. In fact, this is usually the case because the standard library is pre-compiled and hence may not have the same minimum alignment value. It is only when the minimum alignment is relied on in unsafe code that you'd ever run into trouble. The goal of
Similarly for alignment I'd expect something like Btw I don't particularly like the kind of safety argument in the example, but I think it is no worse than just assuming that a target feature is enabled. |
I think it's entirely valid to use a compiler flag for performance concerns like this, but then the flag should probably be considered more as a hint than a guarantee. I actually like clang's naming better for this purpose. I think for language-level guarantees we should probably use I think there are gray areas; I could see there being situations where you need to apply something to a whole crate graph and are forced to rely on either compiler flags or linker tricks. As far as Rust is concerned I like |
Do we actually have test coverage for this? I.e.
Should this be a warning? If this is intended as a hint, then 🤷. |
Doesn't LLVM already insert padding between functions for alignment by default? I believe it aligns to 16 bytes on x86_64 for example. Cranelift also does that. |
This PR adds tests for: //@ revisions: too-high not-power-of-2
//
//@ [too-high] compile-flags: -Cmin-function-alignment=16384
//@ [not-power-of-2] compile-flags: -Cmin-function-alignment=3
//~? ERROR a number that is a power of 2 between 1 and 8192 was expected The implementation uses
The flag is deliberately specified as a minimum. If for some external reason the alignment is already higher, the flag still works as intended: the alignment is at least the specified value.
Yes, there is a per-platform minimum, but RfL wants to bump it higher than than minimum in some cases. From #t-compiler/help > ✔ Alignment for function addresses:
Note the dynamic check for the address actually being aligned in the linked C code. |
That case is not merely a performance optimization, but actually mandatory. For something mandatory a target modifier makes sense to me. |
It is still not unsound, though, and based on the RFC summary:
But maybe the definition of when a flag is a target modifier is broader in practice? I agree that in that case you'd want to effectively guarantee all codegen units use some minimum alignment. |
If this is supposed to be a guarantee it's not obvious to me that a If the goal is that this is for RFL, should RFL just have a different target with a different function alignment setting? Feels like they'd want a different target for things like their different float and vector-register-usage stuff too anyway. With a Obviously with a target flag that inconsistency can't happen, so it just feels nicer to me, and avoids a whole bunch of "well should we have a warning that you compiled a crate with a lower value that a dep?" kinds of questions. |
If you mean adding a new built-in target, then we have avoided adding targets for things like this because there would be way too many due to combinatorial explosion and because it would tie us to new Rust releases if new values are needed in the future. (There was a Linux kernel target years ago, and we didn't use it for those reasons. We ended up talking about "global target features" for things where we just wanted to modify the base target because we knew we would pass the same flags everywhere for a given build, and then finally we had "target modifiers" for things where the ABI really needs to match so it is checked.) |
I don't know that this needs lang approval, personally. But I would say it would be useful to specify whether this option is a best-effort or a guarantee. And, if it's a guarantee, then how does it interact with inlining? In general, I would expect that if you take the address of an inline function then we'd make sure there's an entry point you can take the address of, which any guarantee could then apply to. |
I think #142854 actually changed behavior here. After that PR, the alignment of the crate that contains the function is used, and this gets embedded in the crate metadata as part of the function attributes. Before that PR, the alignment of the crate that codegen'd the function was used. |
We talked about this today in lang triage without specific resolution. We discussed how we may need to see more details here about the use case and the specific ask, and may want to hear from other teams, particularly @rust-lang/opsem and @rust-lang/spec. Some of us have also left individual comments above. |
From an opsem perspective, the only thing that's tricky here is what exactly we guarantee when mixing different values of this flag in crates that are linked together and form one AM. IMO the "obvious" answer is to use the flag set for the crate that contains the function is used (so, the flag indeed acts basically like a crate-level attribute), and I think that is the current implementation, but we have no tests for this case. Also, @tmandry expressed that they would expect a different semantics, though I don't know how to even phrase that one in AM terms. |
tracking issue: #82232
split out from: #140261
Request for Stabilization
Summary
The
-Cmin-function-alignment=<align>
flag specifies the minimum alignment of functions for which code is generated.The
align
value must be a power of 2, other values are rejected.Note that
-Zbuild-std
(or similar) is required to apply this minimum alignment to standard library functions.By default, these functions come precompiled and their alignments won't respect the
min-function-alignment
flag.This flag is equivalent to:
-fmin-function-alignment
for GCC-falign-functions
for ClangThe specified alignment is a minimum. A higher alignment can be specified for specific functions by annotating the function with a
#[align(<align>)]
attribute.The attribute's value is ignored when it is lower than the value passed to
min-function-alignment
.There are two additional edge cases for this flag:
A
min-function-alignment
value lower than the target's minimum has no effect.8192
. Trying to set a higher value results in an error.Testing
History
-Zmin-function-alignment
#134030The -Zmin-function-alignment flag was requested by rust-for-linux #128830. It will be used soon (see #t-compiler/help > ✔ Alignment for function addresses).
Miri supports function alignment since #140072. In const-eval there is no way to observe the address of a function pointer, so no special attention is needed there (see #t-compiler/const-eval > function address alignment).
Originally, the maximum allowed alignment was
1 << 29
, because this is the highest value the LLVM API accepts. However, on COFF the highest supported alignment is only 8192 (see #142638). Practically speaking, that seems more than sufficient for all known use cases. So for simplicity, for now, we limit the alignment to 8192. The value can be increased on platforms that support it if the need arises.r? @workingjubilee
the first commit can be split out if that is more convenient.